package com.twq.collection

import java.io.{BufferedReader, File, FileReader}

import scala.collection.mutable.ArrayBuffer
import scala.util.control.ControlThrowable

/**
 * Created by tangweiqun on 2017/11/15.
 */
object TraversableTest {
  def main(args: Array[String]): Unit = {
    val x = new FileLineTraversable(new File("scala-collection/src/main/resources/test.txt"))
    //2、遍历整个文件test.txt中的每一行，且对每一行按照空格切割成单词
    val words = for {line <- x
                     word <- line.split("\\s+")
                     } yield word
    println(words)

    //3、Traversable虽然无法提供高效的随机访问能力，但是我们可以在必要的时候想办法提前终止遍历
    // take方法用来抽取集合的前n个元素
    //这里遍历x的时候，并没有打印Done iteration file这个日志，
    // 这个是因为Traversable有一种在必要时高效的终止foreach的手段，就是抛出scala.util.control.ControlThrowable
    //这是一种预分配异常，JVM能够高效的抛出和捕获它们
    //但是这个也是有一个缺点：就是take方法实际上会读三行后才抛出异常来终止迭代
    val twoLineWords = for {line <- x.take(2)
                            word <- line.split("\\s+")
                            } yield word
    println(twoLineWords)

    //4、在scala中，有些控制流(比如break等)，是通过继承scala.util.control.ControlThrowable来实现的
    //拜JVM的优化能力所赐，这种做法的性能很不错，单数需要scala程序员在捕获异常时有所注意
    //比如，在用scala的时候不要捕获所有的Throwable异常，请确保rethrow ControlThrowable
    try {

    } catch {
      case ce: ControlThrowable => throw ce
      case t: Exception => println("exception")
    }

    //5: 综上，Traversable中的foreach是所有集合类型中最容易实现的方法
    //但是，foreach方法对很多算法都是次优的选择：
    //它不支持高效的随机访问，同时在提前终止遍历的时候会多取一次元素
    //我们可以用外部迭代器Iterable来解决后面的一个问题

    //6、Traversable所有的方法分类以及含义
    val xs = Seq(1, 2, 4, 1)
    //抽象方法(Abstract Method)
    xs foreach println

    //添加方法(Addition)
    val ys = Seq(2, 3)
    xs ++ ys

    //映射方法(Maps)
    xs map (i => (i + 1).toString())
    xs flatMap (_.to(5))
    xs collect { case i if (i == 2) => i * 3 }

    //转换方法(Conversions)
    x.toArray
    x.toList
    x.toIterable
    x.toSeq
    x.toIndexedSeq
    x.toStream
    x.toSet
    //x.toMap // x必须是key value类型的集合

    //拷贝方法(Copying)
    val buff = new ArrayBuffer[Int]()
    xs.copyToBuffer(buff)
    val arr = new Array[Int](4)
    xs.copyToArray(arr, 1, 2) //从拷贝xs前面两个元素到arr中(从arr的下标为2开始)

    //大小信息方法(Size info)
    xs.isEmpty
    xs.nonEmpty
    xs.size
    xs.hasDefiniteSize //如果Traversable知道有有限的长度则返回true

    //检索元素方法(Element Retrieval)
    xs.head
    xs.headOption
    xs.last
    xs.lastOption
    xs find (_ == 3)

    //子集合方法(Subcollections)
    xs.tail //除了xs.head的所有的元素集合
    xs.tails //Seq(1, 2, 4, 1).tails = Iterator( List(1, 2, 4, 1),List(2, 4, 1),List(4, 1),List(1),Nil)
    xs.init //除了xs.last的所有元素的集合
    xs.slice(1, 3) //List(2, 4)
    xs.take(2)
    xs.drop(2) //除了take(2)外的其他元素的集合
    xs.takeWhile(_ == 1) //从集合的头开始提取符合条件的数据，一旦碰到不符合的数据元素则退出
    xs.dropWhile(_ == 1) //从集合的头开始删除符合条件的数据，一旦碰到不符合的数据元素则退出
    xs.filter(_ == 2) //过滤出符合条件的元素的集合
    xs.withFilter(_ == 2) //不是严格意义上上的filter，不像filter那样会产生新的集合
    xs.filterNot(_ == 2) //过滤出不符合条件的元素的集合

    //切分方法(Subdivisions)
    //在指定的位置对集合介个, 返回 pair of collections (xs take n, xs drop n)
    xs.splitAt(3) //res20: (Seq[Int], Seq[Int]) = (List(1, 2, 4),List(1))
    //根据指定的条件对集合进行切割, 返回 pair of collections (xs takeWhile p, xs.dropWhile p)
    xs.span(_ == 1) //res22: (Seq[Int], Seq[Int]) = (List(1),List(2, 4, 1))
    //返回集合对，第一个集合包括了符合条件的元素的集合，第二个集合包括了不符合条件的元素的集合
    //(xs filter p, xs.filterNot p)
    xs.partition(_ == 1) //res24: (Seq[Int], Seq[Int]) = (List(1, 1),List(2, 4))
    //按照f来group集合成Map
    xs.groupBy(_ + 1) //res25: scala.collection.immutable.Map[Int,Seq[Int]] = Map(2 -> List(1, 1), 5 -> List(4), 3 -> List(2))

    //元素判断方法(Element Conditions)
    xs.forall(_ > 0) //判断集合xs中的所有元素是否大于0
    xs.exists(_ > 2) //判断集合xs中是否有大于2的元素
    xs.count(_ > 2) //计算集合xs中大于2的数量

    //折叠方法(Folds)
    (0 /: xs) (_ + _)
    xs.foldLeft(0)(_ + _)
    (xs :\ 0) (_ + _)
    xs.foldRight(0)(_ + _)
    xs.reduceLeft(_ + _)
    xs.reduceRight(_ + _)

    //Specific Folds
    xs.sum
    xs.product
    xs.min
    xs.max

    //Strings
    val sb = new StringBuilder
    xs.addString(sb, "[", ",", "]")
    xs.mkString("[", ",", "]")
    xs.stringPrefix

    //Views
    xs.view(1, 3)
  }
}

class FileLineTraversable(file: File) extends Traversable[String] {
  //1、trait Traversable就定义了一个接口，就是foreach
  //这个属于内部迭代，无法提供高效的随机访问能力
  override def foreach[U](f: (String) => U): Unit = {
    println("Opening file")
    val input = new BufferedReader(new FileReader(file))
    try {
      var line = input.readLine()
      while (line != null) {
        f(line)
        line = input.readLine()
      }
      println("Done iteration file")
    } finally {
      println("Closing file")
      input.close()
    }
  }

  override def toString(): String = s"{Lines of ${file.getAbsolutePath}}"
}
